using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.IO;
using System.Runtime.Remoting;
using System.ComponentModel;
using System.Xml.Serialization;

namespace NBusiness.Utilities
{
	/// <summary>
	/// Use the static method GetPlugins to create instances of Plugin objects
	/// and call the Instance property to get an instance of the object this
	/// plugin found.
	/// </summary>
	[Serializable]
	internal class Plugin
	{
		#region Member Fields and Properties
		private Type _Type = Type.Missing.GetType();
		/// <summary>
		/// The type that this plugin will Instantiate.
		/// </summary>
		[ReadOnly(true)]
		public Type Type
		{
			get { return this._Type; }
			set
			{
				_Type = value;
			}
		}
		private object _Instance = null;
		/// <summary>
		/// An instance of an object this plugin represents.
		/// </summary>
		[ReadOnly(true)]
		public object Instance
		{
			get
			{
				if (_Instance == null) LoadInstance();
				return _Instance;
			}
			set { _Instance = value; }
		}
		private object[] _Args = null;
		/// <summary>
		/// Arguments to be used while instantiating the object this plugin
		/// represents.
		/// </summary>
		public object[] Args
		{
			get { return this._Args; }
			set { this._Args = value; }
		}

		#endregion

		#region Constructor
		/// <summary>
		/// Create an empty plugin.
		/// </summary>
		public Plugin() { }
		/// <summary>
		/// Create a plugin representing a given type.
		/// </summary>
		/// <param name="Type">The Type this plugin represents.</param>
		public Plugin(Type Type)
		{
			this._Type = Type;
		}
		#endregion

		#region Load Instance
		/// <summary>
		/// Load the instance of the Type this plugin represents.
		/// </summary>
		public void LoadInstance()
		{
			_Instance = Activator.CreateInstance(_Type, _Args);
		}
		#endregion

		#region Object Overrides
		/// <summary>
		/// Return the ToString() value of the Instance if possible, else return
		/// base.ToString()
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			if (_Instance != null) return Instance.ToString();
			else return base.ToString();
		}
		/// <summary>
		/// Call Equals on the Instance if possible else call base.Equals
		/// </summary>
		/// <param name="obj"></param>
		/// <returns></returns>
		public override bool Equals(object obj)
		{
			if (_Instance != null) return Instance.Equals(obj);
			else return base.Equals(obj);
		}
		/// <summary>
		/// Get the hash code of the Instance if possible else call base.GetHashCode()
		/// </summary>
		/// <returns></returns>
		public override int GetHashCode()
		{
			if (_Instance != null)return Instance.GetHashCode();
			else return base.GetHashCode();
		}
		#endregion

		#region static GetPlugins
		/// <summary>
		/// Gets a List of Plugins in all satelite assemblies found in the path of 
		/// the EntryAssembly of Type PluginType.
		/// </summary>
		/// <param name="PluginType">The type that all plugin Types should inherit from.</param>
		/// <returns>A list of plugins.</returns>
		public static Plugin[] GetPlugins(Type PluginType)
		{
			Assembly a = Assembly.GetCallingAssembly();
            string local = a != null ? null : a.Location.Substring(0, a.Location.LastIndexOf('\\'));

			return Plugin.GetPlugins(PluginType, Directory.GetCurrentDirectory(), local);
		}
		/// <summary>
		/// Gets a List of Plugins in all satelite assemblies found in the given path  
		/// of Type PluginType.
		/// </summary>
		/// <param name="Path">The path to search for assemblies.</param>
		/// <param name="PluginType">
		/// The Type that all plugin Types should inherit from. This type must implement IPlugin.
		/// </param>
		/// <returns>A list of plugins.</returns>
        public static Plugin[] GetPlugins(Type PluginType, params string[] Paths)
        {
            List<Plugin> plugins = new List<Plugin>();
            List<Assembly> assemblies = new List<Assembly>();

            foreach (string path in Paths)
            {
                if (path != null && Directory.Exists(path))
                {
                    foreach (string file in Directory.GetFiles(path))
                    {
                        string ext = Path.GetExtension(file);
                        if (ext == ".dll" || ext == ".exe")
                        {
                            try
                            {
                                Assembly assembly = Assembly.LoadFile(path);
                                assemblies.Add(assembly);
                            }
                            catch (Exception) { }
                        }
                    }
                }
            }
            plugins.AddRange(GetPlugins(PluginType, assemblies.ToArray()));

            return plugins.ToArray();
        }

        public static Plugin[] GetPlugins(Type pluginType, params Assembly[] assemblies)
        {
            Dictionary<string, Plugin> plugins = new Dictionary<string, Plugin>();
			try
			{
                foreach (Assembly assembly in assemblies)
                {
                    foreach (Type t in assembly.GetTypes())
                    {
                        if ((pluginType.IsInterface && t.GetInterface(pluginType.Name) != null) ||
                            t.IsSubclassOf(pluginType))
                        {
                            Plugin p = new Plugin(t);
                            plugins[t.FullName] = p;
                        }
                    }
                }
			}
			catch (Exception) { }
			
			Plugin[] array = new Plugin[plugins.Count];
			plugins.Values.CopyTo(array, 0);
			return array;
		}
		#endregion
	}
}
